module SDLBridge.SDLGraphics;
	private import std.string;
	private import std.file;
	private import std.path;
	private import std.cstream;
	private import std.ctype;
	private import std.math;
	private import std.math2;
	private import std.c.stdio;
	private import std.format;
	private import std.utf;
	private import std.random;
private import SDL;
private import SDL_mixer;
private import opengl;
private import openglu;
private import SDLBridge.SDLGraphics;
private import SDLBridge.SDLTexture;
private import SDLBridge.SDLSprite;

private import Graphics.IGraphics;
private import Graphics.ZSorter;
private import System.Console;

class SDLGraphics : IGraphics{
	private int _blendMode;
	private Color _color;
	private float _scaleX , _scaleY;
	private ZSorter zsorter;
	private int _screenWidth = 640 , _screenHeight = 480 ;
	private SDL_Surface *_screen;
	
	private int _sptr;
	private const int MAX_SPRITES = 1024;
	private SpritePool!(SDLSprite) _pool;
	private Rectangle _viewrect;
	public int screenWidth() {return _screenWidth;}
	public int screenHeight(){return _screenHeight;}

	public this( SDL_Surface *screen , int w , int h){
		_screen = screen;
		_screenWidth  = w;
		_screenHeight = h;
		init();
	} //
	
	
	
	
	private void init(){
		_sptr = 0;
//		_pool = new SpritePool( delegate ISprite(){return new SDLSprite();} , MAX_SPRITES );
		_pool = new SpritePool!(SDLSprite)( MAX_SPRITES );
		_viewrect = new Rectangle( 0 , 0 , _screenWidth , _screenHeight );
		zsorter = new ZSorter();
		blendMode(ALPHA_NORMAL);

		resetScale();
	} //

	private void resetScale() {
		glLoadIdentity();
		glScalef( 1f / _screenWidth , -1f / _screenHeight , 1 );
		glTranslatef( -_screenWidth , -_screenHeight , 0 );
	} //

	public void setViewport( float x1 , float y1 , float x2 , float y2 ){
		_viewrect.x  = x1;
		_viewrect.y  = y1;
		_viewrect.x2 = x2;
		_viewrect.y2 = y2;

		glLoadIdentity();
		glScalef(   2f / _viewrect.width 
				,  -2f / _viewrect.height 
				,   1f );
		glTranslatef(  -viewrect.width  *.5 - x1 //*.5
					,  -viewrect.height *.5 - y1 //*.5
					, 0 );
/*
		float dx = ( _screenWidth  / _viewrect.width  );
		float dy = ( _screenHeight / _viewrect.height );
		float scaleX = ( _screenWidth  * dx );
		float scaleY = ( _screenHeight * dy );
		float scaleX = ( _screenWidth  * dx );
		float scaleY = ( _screenHeight * dy );
		glViewport( cast(int)(-x1*dx) , cast(int)( y1*dy + _screenHeight - scaleY ), cast(int)scaleX  , cast(int)scaleY );
*/
		
	} //
	public Rectangle viewrect() {
		return _viewrect;
	} //
	public void viewrect( Rectangle rect ){
		_viewrect = rect;
		setViewport( rect.x , rect.y , rect.x2 , rect.y2 );
	} //

	public int blendMode(){
		return _blendMode;
	} //

	public void blendMode(int mode){
		_blendMode = mode;
		switch(mode)
		{
		case ALPHA_DISABLE:
			glDisable(GL_BLEND);
			break;
		default:
		case ALPHA_NORMAL:
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA);
			break;
		case ALPHA_ADD:
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA , GL_ONE);
			break;
		}
	} //

	
	public Color backgroundColor(){
		return _color;
	} //
	public void backgroundColor( Color c ){
		_color = c;
	} //
	public void clear(){
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	} //
	public void drawRect(	float x , float y , 
							float width , float height , 
							Color color , 
							float rotate = 0 ,
							float lineWidth = 1 ,
	){
		if ( lineWidth < 1 ) return;
		unbindTexture();

		glPushMatrix();
		glColor4ub( cast(ubyte)color.r, cast(ubyte)color.g, cast(ubyte)color.b, cast(ubyte)color.a );
		glTranslatef( x , y , 0);
		if (rotate != 0){
			float hw = width * .5 , hh = height * .5;
			glTranslatef(  hw ,  hh , 0 ); 
			glRotatef( rotate ,   0 , 0 , -1 ); // 左回り
			glTranslatef( -hw , -hh , 0 );
		}
			glLineWidth(lineWidth);
			glBegin(GL_LINE_LOOP);
		{
			{
				glVertex3f( 0     , 0     , 0 );
			 	glVertex3f( width , 0     , 0 );
				glVertex3f( width , height, 0 );
				glVertex3f( 0     , height, 0 );
			}
			glEnd();
		}
		glPopMatrix();

	} //

	public void drawFilledRect(   float x , float y , 
							float width , float height , 
							Color color , 
							float rotate = 0
	){
		unbindTexture();
	
		glPushMatrix();
		glColor4ub( cast(ubyte)color.r, cast(ubyte)color.g, cast(ubyte)color.b, cast(ubyte)color.a );
		glTranslatef( x , y , 0 );
		if (rotate != 0){
			glTranslatef( width / 2 , height / 2 , 0);
			// 左回り
			glRotatef( rotate , 0 , 0 , -1 );
			glTranslatef( -width / 2 , -width / 2 , 0 );
		}
			glBegin(GL_POLYGON);
		{
			{
				glVertex3f( 0     , 0     , 0 );
			 	glVertex3f( width , 0     , 0 );
				glVertex3f( width , height, 0 );
				glVertex3f( 0     , height, 0 );
			}
			glEnd();
		}
		glPopMatrix();

	} //

	public void drawLine( float x1 , float y1 , float x2 , float y2 , Color color , float lineWidth = 1 ){
		if ( lineWidth < 1 ) return;
		unbindTexture();
		glPushMatrix();
		glColor4ub( cast(ubyte)color.r, cast(ubyte)color.g, cast(ubyte)color.b, cast(ubyte)color.a );
		glTranslatef( x1 , y1 , 0 );

		glLineWidth( lineWidth );
		glBegin(GL_LINES);
		{
			glVertex3f( x1 , y1 , 0 );
			glVertex3f( x2 , y2 , 0 );
		}
		glEnd();

		glPopMatrix();
	} //

	public void drawImage( ITexture tx , float x , float y , float width , float height , float rotate = 0){
		Color color		= Color.create( 0xffffffffu );
		drawImage( tx , x , y , width , height , color , rotate );
	} //

	public void drawImage( ITexture tex , float x , float y , float width , float height 
							, Color color , float rotate = 0){
//		SDLTexture tex  = cast(SDLTexture)tx;
//		if ( tex is null ) return;
		Rectangle clip  = tex.clipRectangle;
		float clipLeft  = clip.x     , clipTop	  = clip.y 
			, clipWidth = clip.width , clipHeight = clip.height;
	
		float dx = width  / clipWidth;
		float dy = height / clipHeight;
		float w  = dx * clipWidth   ;
		float h  = dy * clipHeight  ;
		bindTexture( tex );

		glPushMatrix();
		glColor4ub( cast(ubyte)color.r, cast(ubyte)color.g, cast(ubyte)color.b, cast(ubyte)color.a );
		glTranslatef( x , y , 0 );
		if ( rotate != 0 ) {
			glTranslatef( w * .5 , h * .5 , 0 );
			glRotatef( rotate, 0, 0, -1 );// 左回り
			glTranslatef( w *-.5 , h *-.5 , 0 );
		}

		float  w1 = 1.0F / tex.width
			  ,h1 = 1.0F / tex.height;

		float s = clipLeft    / tex.width;
		float t = clipTop     / tex.height;
		float u = clipWidth   / tex.width;
		float v = clipHeight  / tex.height;

		
		glBegin(GL_POLYGON);
		{
			glTexCoord2f(s    , t    ); glVertex3f( 0 , 0 , 0 );
			glTexCoord2f(s + u, t    ); glVertex3f( w , 0 , 0 );
			glTexCoord2f(s + u, t + v); glVertex3f( w , h , 0 );
			glTexCoord2f(s    , t + v); glVertex3f( 0 , h , 0 );
		}
		glEnd();
		
		glPopMatrix();
//		unbindTexture();
	
	} //

	//	void drawImage( ITexture tx , float centerX , float centerY , float width , float height);

	private SDLTexture fontTexture = null;
	private static string FONT_PATH = "resource/font/font.bmp";
	private const Uint32 FONT_MASK = 0x00ff00u;
	public void drawText(string text , int x, int y, Color color , float fontWidth = 32 , float fontHeight = 48 ){
		const int WIDTH  = 32; // フォントの幅
		const int HEIGHT = 48; // フォントの高さ
		const int COLUMN = 16; // フォントテクスチャの一行の文字数
		char decoder[]   = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.()[]#$%&'\"!?^+-*/=;:";
		if ( fontTexture is null ) {
			Console.write("loading font bitmap...");
			fontTexture = SDLTexture.createTexture( "" , FONT_PATH, Color.create( FONT_MASK ) , false );
			fontTexture.setFrameSize( WIDTH , HEIGHT , 0 );
			Console.write("OK\n");
		}
		bindTexture( fontTexture );
		glColor4ub( cast(ubyte)color.r , cast(ubyte)color.g , cast(ubyte)color.b , cast(ubyte)color.a );
		
		float zoomX = fontWidth  / WIDTH;
		float zoomY = fontHeight / HEIGHT;
		float u = WIDTH  / cast(float)fontTexture.width;
		float v = HEIGHT / cast(float)fontTexture.height;
		float w = WIDTH  * zoomX; // 実際の幅
		float h = HEIGHT * zoomY; // 実際の高さ
		foreach(c; text)
		{
			if ( !isascii( c ) ) continue;    // ascii文字じゃないです
			int idx = find( decoder , c ); // 文字のインデックスを取得
			if (c == ' ' | idx < 0) {
				x += cast(int)( WIDTH * zoomX); // スペース
				continue;
			}
			
			glPushMatrix();
			glTranslatef( x , y, 0 );
			
			// テクスチャ座標を取得
			float s = (idx % COLUMN) * u;
			float t = (idx / COLUMN) * v;
			
			glBegin(GL_POLYGON);
			glTexCoord2f(s,     t);     glVertex3f(0, 0, 0);
			glTexCoord2f(s + u, t);     glVertex3f(w, 0, 0);
			glTexCoord2f(s + u, t + v); glVertex3f(w, h, 0);
			glTexCoord2f(s,     t + v); glVertex3f(0, h, 0);
			glEnd();
			
			glPopMatrix();
			
			x += cast(int)(WIDTH * zoomX);
		}
//		unbindTexture();
	} //


	public ISprite getSprite(){
		return cast(ISprite)_pool.get();
	} //

	public bool addDrawingEntry( IDrawingEntry o ){
		return zsorter.add( o );
	} //
	public bool draw(){
		zsorter.draw( this );
		SDL_GL_SwapBuffers();
		return true;
	} //
	
	public bool dispose(){
		SDLTexture.dispose();
		if( fontTexture )
			fontTexture.dispose();
		return true;
	} //

	public ITexture createTexture( string key , string fileName , bool share = true ){
		return SDLTexture.createTexture( key , fileName , share );
	} //

	public ITexture createTexture( string key , string fileName , Color keyColor , bool share = true){
		Console.writeln("SDLGraphics createTexture %s=%s" , key , fileName);
		return SDLTexture.createTexture( key , fileName , keyColor , share );
	} //

	public ITexture createTexture( string key , string fileName , int keyX , int keyY , bool share = true){
		return SDLTexture.createTexture( key , fileName , keyX , keyY , share );
	} //


	/**
	 * バインド
	 */
	private static int _lastTextureId = -1;
	private void bindTexture( ITexture tex ) {
		if ( _lastTextureId != tex.id ) glBindTexture(GL_TEXTURE_2D, tex.id );
		_lastTextureId = tex.id;
		glEnable(GL_TEXTURE_2D);
	} //
	/**
	 * バインド解除
	 */
	private static void unbindTexture() {
		glDisable(GL_TEXTURE_2D);
		_lastTextureId = -1;
	} //

} //

